package com.jswdwsx.esign.service

import com.jswdwsx.esign.cloud.enums.FlowStatusEnum
import com.jswdwsx.esign.cloud.enums.IdentityTypeEnum
import com.jswdwsx.esign.cloud.enums.toDocNameEnum
import com.jswdwsx.esign.cloud.request.MerchantInfoReq
import com.jswdwsx.esign.cloud.request.SignReq
import com.jswdwsx.esign.cloud.response.FlowStatusResp
import com.jswdwsx.esign.commom.ResEnum
import com.jswdwsx.esign.config.EsignProperties
import com.jswdwsx.esign.entity.*
import com.jswdwsx.esign.exception.EsignBusinessException
import com.jswdwsx.esign.repo.FlowRepository
import com.jswdwsx.esign.repo.FlowTemplateRepository
import com.jswdwsx.esign.repo.RequestRepository
import com.jswdwsx.esign.service.dto.CreateAccountDTO
import com.jswdwsx.esign.service.enums.EsignSignPlatformEnum
import com.jswdwsx.esign.service.mapper.Mapper
import org.slf4j.LoggerFactory.getLogger
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.net.URLEncoder


@Service
@Transactional
class SignService(
    private val flowTemplateRepository: FlowTemplateRepository,
    private val requestService: RequestService,
    private val requestRepository: RequestRepository,
    private val flowRepository: FlowRepository,
    private val flowService: FlowService,
    private val accountService: AccountService,
    private val organizationService: OrganizationService,
    private val fileService: FileService,
    private val esignProperties: EsignProperties
) {

    private val logger = getLogger(SignService::class.java)

    /**
     *  创建签署流程并获取签署url（每个appId、appSerialNumber）对应唯一一个request
     *  appSerialNumber应由业务系统每次发起签署请求时生成，不能重复。
     *  @param signReq
     */
    fun sign(signReq: SignReq): String? {

        // 重复请求直接拒绝
        requestRepository.getByAppIdAndAppSerialNumber(signReq.appId, signReq.appSerialNumber)?.let {
            throw EsignBusinessException(ResEnum.RequestAlreadyExist)
        }

        // 保存请求参数
        val request = Mapper.map(signReq)
        requestRepository.save(request).also {
            logger.info("request:${it}")
        }

        // 开始处理
        return signInit(request)
    }

    fun signQuery(appId: String, appSerialNumber: String): FlowStatusResp? {
        // 获取request
        val requestId = requestRepository.getByAppIdAndAppSerialNumber(appId, appSerialNumber)?.id
            ?: throw EsignBusinessException(ResEnum.RequestDoesNotExist)

        // 获取flow
        val flow =
            flowRepository.findByIdOrNull(requestId) ?: throw EsignBusinessException(ResEnum.FlowDoesNotExist)

        return flowService.queryFlow(flow.esignFlowId).let {
            requestService.finish(flow, it.flowStatus)
            FlowStatusResp(
                appSerialNumber = appSerialNumber,
                flowStatus = when (it.flowStatus) {
                    0 -> FlowStatusEnum.NEW
                    1 -> FlowStatusEnum.Waiting
                    2 -> FlowStatusEnum.Success
                    else -> FlowStatusEnum.Fail
                },
                desc = it.flowDesc,
                docsUrl = flow.ossFileUrl?.associateBy({ doc -> doc.fileName.toDocNameEnum() }, { doc -> doc.fileUrl })
            )

            /* E签宝定义状态
            * 流程状态,0->草稿 1->签署中 2->完成 3->撤销 4->终止 5->过期 6->删除 7->拒签

            *  本项目定义状态
            Empty,   //新建创，暂未向e签宝初始化
            Waiting, //已初始化，待签署
            Success, //签署完成
            Fail    // 流程终止
            */
        }
    }

    fun signInit(request: RequestPO): String? {
        val requestId = request.id ?: throw EsignBusinessException(ResEnum.ParamErr)
        logger.info("准备初始化签署流程,RequestId:${requestId}")

        val template = flowTemplateRepository.findByIdOrNull(request.templateId)
            ?: throw EsignBusinessException(ResEnum.TemplateDoesNotExist)

        logger.info("开始执行步骤一：获取（创建）用户（和企业）")
        val (signer, orgPO, authorizedAccountId) = checkAndCreateAccount(request)

        logger.info("开始执行步骤二：根据模板创建文件")
        val fileMaps = fileService.createFileByTemplate(request, template)

        logger.info("开始执行步骤三：创建并配置签署流程")
        val flow: FlowPO = flowService.flowInit(template, request, fileMaps, signer, authorizedAccountId)

        logger.info("开始执行步骤四：获取签署url")
        val signUrl: String? = when (template.signPlatform) {
            EsignSignPlatformEnum.OPEN_SERVICE -> {
                flowService.getSignUrl(flow.esignFlowId, signer, orgPO?.esignOrgId)
            }
            EsignSignPlatformEnum.ALIPAY_SIGN -> {
                getAlipaySignUrl(flow.esignFlowId, signer.accountId)
            }
        }

        logger.info("签署url:{$signUrl}")

        // update flowPO(with positive lock)
        val updated = flowRepository.updateFlow(requestId, flow.version, FlowStatusEnum.Waiting, signUrl)
        if (updated < 1) {
            logger.warn("flowPO already been update, roll back by positive lock")
            throw EsignBusinessException(ResEnum.FlowAlreadyStart)
        }
        requestRepository.updateRequest(requestId, RequestStatusEnum.Waiting)

        return signUrl
    }

    /**
     * 拼接支付宝小程序签署url
     */
    fun getAlipaySignUrl(
        esignFlowId: String,
        accountId: String
    ): String {
        val appId = "2019042964339413"
        val page = "/pages/startup/index"
        val encodedPage = URLEncoder.encode(page, "UTF-8")
        val query =
            "env=${esignProperties.env}&page=sign&flowId=${esignFlowId}&signerId=${accountId}&forwardHome=false"
        val encodedQuery = URLEncoder.encode(query, "UTF-8")
        return "alipays://platformapi/startapp?appId=${appId}&query=$encodedQuery&page=$encodedPage"
    }

    fun checkAndCreateAccount(requestPO: RequestPO): Triple<UserPO, OrgPO?, String> {
        val createAccountDTO = CreateAccountDTO(requestPO)
        return checkAndCreateAccount(createAccountDTO)
    }

    fun checkAndCreateAccount(createAccountDTO: CreateAccountDTO): Triple<UserPO, OrgPO?, String> {

        val signer = accountService.checkAndCreateUser(createAccountDTO)

        val authorizedAccountId: String
        val orgPO: OrgPO?
        if (IdentityTypeEnum.ORG == createAccountDTO.identityType) {
            orgPO = organizationService.checkAndCreateOrg(signer, createAccountDTO)
            authorizedAccountId = orgPO.esignOrgId
        } else {
            orgPO = null
            authorizedAccountId = signer.accountId
        }
        return Triple(signer, orgPO, authorizedAccountId)
    }

    /**
     * 商户信息初始化
     */
    fun initMerchant(merchantInfoReq: MerchantInfoReq): String? {
        val createAccountDTO = CreateAccountDTO(merchantInfoReq)

        val (user, org, authorizedAccountId) = checkAndCreateAccount(createAccountDTO)

        val seal: SealPO
        seal = if (createAccountDTO.identityType == IdentityTypeEnum.PERSON) {

            // 查找或创建默认个人印章
            accountService.checkAndCreateDefaultPersonalSeal(user)
        } else {
            if (org == null) {
                throw EsignBusinessException(ResEnum.CreateMerchantOrgFail)
            }

            // 查找或创建默认企业印章
            organizationService.checkAndCreateDefaultOrgSeal(org)
        }

        // 设置静默签署
        if (!accountService.setSilentSign(authorizedAccountId)) {
            logger.error("设置静默签署失败,authorizedAccountId:${authorizedAccountId}")
            throw EsignBusinessException(ResEnum.CreateMerchantOrgFail)
        }

        return seal.id
    }
}